#include<stdio.h>

/*
		参考文献：https://blog.csdn.net/2302_80198073/article/details/136966057
				 https://www.runoob.com/cprogramming/c-memory-management.html
	动态内存管理：
		动态内存管理是指在程序运行时，根据需要动态地分配和释放内存的过程。
		与静态内存分配（如全局变量或局部变量）不同，
		动态内存分配允许程序在运行时根据实际需求灵活地调整内存的使用。
		它是C语言中一项重要的编程任务，它使得程序在运行时能够灵活地分配和释放内存，更好地适应不同的运行条件，通过动态内存管理，我们可以使用内存更高效、更灵活。
	
	 为了方便解释我们解开理解：
		动态：即我们可以将申请的空间自由的变大变小以适应需求；

		内存：即我们申请的对象是内存空间；

		管理：我们需要对申请的内存空间进行管理操作；

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

	动态内存管理的来源：
		int b = 1;
		int arr[5] = {0};

		从上边的实例我们可以看出，我们创建变量是开辟空间的大小是固定的；
		当我们在船舰数组的时候，必须指定数组的长度，并且数组空间一旦确定了大小将不能调整其大小。
		但是有时候我们需要的空间大小是在程序运行的时候才能知道，那么上述创建空间大小的方式就不太适合了
		比如说：
			
			编程时，如果你预先知道数组的大小，那么定义数组时就比较容易。
			例如，一个存储人名的数组，它最多容纳 100 个字符，可以定义数组，如下所示：
			
			char name[100];

			但是，如果预先不知道需要存储的文本长度，例如想存储有关一个主题的详细描述。
			在这里，我们需要定义一个指针，该指针指向未定义所需内存大小的字符，后续再根据需求来分配内存，如下所示：
			#include <stdio.h>
			#include <stdlib.h>
			#include <string.h>

			int main()
			{
			   char name[100];
			   char *description;//定义了一个指针

			   strcpy(name, "Zara Ali");//将"Zara Ali"拷贝进name数组中

  
			description = (char*)malloc(200 * sizeof(char));//使用malloc函数动态分配了200个字节，并将其首元素的地址赋值给description,这里(char*)是对返回值进行强制类型转换，使其类型为指向字符的指针。
			if (description == NULL)   //检查malloc函数是否成功分配了内存。如果分配失败，malloc将返回NULL。在程序中，如果分配失败，使用fprintf(stderr, ...)将错误信息输出到标准错误流。
			{
				fprintf(stderr, "Error - unable to allocate required memory\n");
			}
			else  //如果内存分配成功，使用strcpy函数将字符串"Zara ali a DPS student in class 10th"复制到description指针指向的内存空间中。
			{
				strcpy(description, "Zara ali a DPS student in class 10th");
			}
			printf("Name = %s\n", name);
			printf("Description: %s\n", description);
			free(description)
}
	
		因此引入了动态内存开辟的方式，让程序员自己可以申请和释放空间，这也就是为什么要有动态内存管理的原因

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
		现在我们已经了解动态内存管理的基本概念和为什么要有动态内存管理，那么接下来就是如何去申请动态内存，并且对其进行操作了。

       对于申请和释放动态内存空间，我们有这三个函数实现：malloc、free、calloc、realloc
	


*/

//头文件区域

#include<stdio.h>
#include<stdlib.h>
#include<string.h>



//程序1
//使用malloc函数分配<malloc函数解析见同筛选器——malloc函数解析>
//使用free函数释放内存空间<free函数解析见同筛选器——free函数解析>
void test1()
{
	int* ptr;
	ptr = (int*)malloc(sizeof(int));
	if (ptr == NULL)
	{
		printf("内存分配失败");
	}

	printf("请输入一个整数：");
	scanf("%d",ptr);
	printf("你输入的整数是：%d\n", *ptr);
	free (ptr);//内存释放
}

void test2()
{
	int n;
	printf("请输入数组宽度:");
	scanf("%d", &n);

	int* ptr = (int *)malloc(sizeof(int) * n);
	if (ptr == NULL)
	{
		printf("内存分配失败");
		exit(1);
	}

	//传进数组
	printf("传输字符%d进数组：", n);
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &ptr[i]);
	}

	printf("你输的数组包含以下数字：");
	for (int i = 0; i < n; i++)
	{
		printf("%d", ptr[i]);
	}

}


//导致内存泄露的两个问题
/*
*	实例1：如果申请的动态内存空间没有被及时释放会怎么样?<！！！warning！！！这段代码禁止运行，会导致电脑死机>

	#include<stdio.h>
	#include<stdlib.h>

	int main()
	{
		while(1)
		{
			malloc(1024);//利用循环反复申请内存空间
		}
	}

	如果申请的动态内存空间没有被及时释放，会导致内存泄漏。
	内存泄漏是指程序在申请内存后，由于某种原因没有释放或释放不彻底，导致这部分内存无法被其他程序或进程使用。
	长时间运行的程序如果存在内存泄漏，可能会耗尽可用内存，从而导致系统运行缓慢甚至崩溃。
	为了避免内存泄漏，应该确保在不再需要动态分配的内存时，及时调用 free 函数来释放它。

---------------------------------------------------------------------------------------------------------------------------------

	实例2：丢失代码块
	#include<stdio.h>
	#include<stdlib.h>

	int main()
	{
		int* ptr;
		int num;

		ptr = (int*)malloc(sizeof(int))
		if(ptr == NULL)
		{
			printf("分配内存失败!\n");
			exit(1);
		}

		printf("请输入一个整数：");
		scanf("%d",ptr);

		printf("你输的整数是：%d",*ptr);

		ptr = &num;
		printf("你输的整数是：%d"。*ptr);

		free(ptr);
		//在ptr = &num;这一行，ptr被重新赋值为指向局部变量num的地址，而不是最初动态分配的内存空间的地址。
		因此，在free(ptr);这一行，实际上试图释放的是一个局部变量的地址，局部变量不存储在堆中，存储在栈中，这是不合法的，会导致未定义行为，可能是程序崩溃。
*/


//程序3
//使用memset初始化malloc申请的数据
/*
	我们可以发现malloc申请的内存块在初始化的时候并没有自动初始化为0，而是随机数值，这样会产生一个问题，当你申请一个动态内存过大的时候，全部打印出来，就会发现一堆不知道的数据，全部打印出来就会出现不一样的效果，
	不信？那我们举个例子吧

*/


//未初始化malloc函数打印malloc申请内存块的数据
void test3_1()
{
	printf("请输入你要存储数据的数量：");
	int num;
	scanf("%d", &num);

	int* ptr = (int*)malloc(num * sizeof(int));
	if (ptr == NULL)
	{
		printf("内存未分配完成");
		exit(1);
	}

	//初始化计数器
	int count = 0;
	int n = num;

	printf("请输入%d个数字(以-1结束):", num);
	do
	{
		scanf("%d", &ptr[count]);
		if (ptr[count] == -1)
		{
			break;
		}
		count++;

	} while (n--);

	printf("你输入的数字是：");
	for (int i = 0; i < num; i++)
	{
		printf("%d ", ptr[i]);
	}

}

//这回信了没?
//使用memset申请动态内存分配

void test3_2()
{
	int n = 10;
	int* ptr = (int*)malloc(n * sizeof(int));

	if (ptr == NULL)
	{
		printf("内存分配失败");
		exit(1);
	}
	memset(ptr,0,n*sizeof(int));
	for (int i = 0; i < n; i++)
	{
		printf("ptr[%d] = %d\n", i, ptr[i]);
	}

	free(ptr);
}

//使用memcpy函数辅助指定的字符到新的动态数组中
//memcpy使用法则：memset(复制到的内存地址<你也可以理解为新的动态数组>,源内存地址<旧的动态数组>,要拷贝的数量*类型大小)
//来个小插曲,C语言真的有动态数组吗？哎，我可没说动态内存是动态数组喔，在 C 语言中，并没有直接支持动态数组的语法，但可以通过使用动态内存分配来实现类似动态数组的功能。

void test4()
{
		// 定义源数组和目标数组
		int src[] = { 1, 2, 3, 4, 5 };
		int dest[4];

		// 计算要复制的字节数
		size_t n = sizeof(src); 

		// 使用 memcpy 复制内存
		memcpy(dest, src, n);

		// 输出目标数组的值
		printf("源数组: ");
		for (int i = 0; i < 5; i++) {
			printf("%d ", src[i]);
		}
		printf("\n");

		printf("目标数组: ");
		for (int i = 0; i < 5; i++) {
			printf("%d ", dest[i]);
		}
		printf("\n");

}






int main()
{
	test4();
	return 0;
}
